home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
pcr
/
pcr4_4.lha
/
UTILITIES
/
makescript.c
< prev
Wrap
C/C++ Source or Header
|
1989-01-16
|
19KB
|
918 lines
#ifndef lint
static char rcsid[] = "@(#)$Header: /usr/src/local/bin/RCS/makescript.c,v 1.9 86/03/05 03:09:45 bin Exp $";
#endif
/*
* Makescript - make a shell script which extracts files from itself
*
* Usage:
* makescript [-k] [-p] [-t] [-v version] [-a file || -o file] file1 file2 ... filen
*
* Authors: Chris Torek, Liz Allen @ University of Maryland
*
* Last Edit: Alan Ishigo @ Xerox on Oct 12, 1988
*
* Compilation command:
% cc -O -s -o makescript makescript.c
*/
/* Alan Ishigo -- Made the following changes to Makescript:
*
* o Added the option to preserve a and m times of files [-k]
*
* o Combined code from error.c into one file to ease further modifications
*
* o Added version switch [-v <version>] which will allow a version stamp
* to be put into the shar
*
* o Added checksum capabilities to enable stronger checking of files
*
* o Added procs "AddTimeKeeper" and "AddChksum" which contain the source
* for the utime setter and the checksum generator respectively
*
* Compilation command: %cc -O -o makescript makescript.c
*
*/
#define max(a,b) ((a) > (b) ? (a) : (b))
#include <stdio.h>
#include <a.out.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#define LIBNDIR /* non-libndir is probably no longer functional anyway */
int RootID, BinID, DaemonID;
FILE *outfile; /* Script being built */
int Level; /* Nest level */
char WDir[BUFSIZ]; /* Working directory */
char serrbuf[BUFSIZ]; /* buffer for stderr */
int AssumeNoDataFiles; /* true => assume all non-executable files
are text */
int Errors; /* total number of errors detected */
int keeptimes; /* flag whether or not to preserve a and m times */
extern int errno; /* system call error number */
extern int optind; /* argv[] index from getopt */
extern char *optarg; /* argument pointer from getopt */
char *_argv0; /* argv[0], set by C startup code */
char *tail(), *protect();
FILE *popen();
main(argc, argv)
register int argc;
register char **argv;
{
register int c;
int Append = 0, existed, rflag = 1;
char *outfname = 0;
char *version = 0;
keeptimes = 0;
setbuf(stderr, serrbuf);
while ((c = getopt(argc, argv, "a:o:v:kpt")) != EOF) {
switch (c) {
case 'a':
Append++; /* ...and fall thru to 'o' */
case 'o':
if (outfname)
goto usage;
outfname = optarg;
break;
case 'v':
if (version) goto usage;
version = optarg;
break;
case 'k':
keeptimes = 1;
break;
case 'p':
rflag = 0;
break;
case 't':
AssumeNoDataFiles++;
break;
case '?':
goto usage;
}
}
if (optind >= argc) {
usage:
fprintf(stderr, "\
Usage: %s [-k] [-p] [-t] [-v <version>] [-a|-o <scriptname>] file1 [ file2... ]\n",
*argv);
exit(1);
}
if (outfname == 0)
outfile = stdout;
else {
existed = !access(outfname, 0);
if (existed && !Append) { /* file exists... */
if (isatty(fileno(stdin))) {
char answer[20];
fprintf(stderr, "%s exists. Overwrite? ",
outfname);
fflush(stderr);
fgets(answer, sizeof answer, stdin);
if (*answer != 'y' && *answer != 'Y') {
fprintf(stderr, "Whatever you say.\n");
exit(1);
}
}
}
outfile = fopen(outfname, Append ? "a" : "w");
if (outfile == NULL)
error(1, errno, "can't open %s", outfile);
}
GetIDs();
if (rflag) {
register FILE *f = popen("/bin/pwd", "r");
fread(WDir, 1, sizeof WDir, f);
WDir[strlen(WDir) - 1] = 0;
pclose(f);
}
if (!existed || !Append)
fprintf(outfile, "\
: Run this shell script with \"sh\" not \"csh\"\n\
PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH\n\
export PATH\n\
all=FALSE\n\
if [ x$1 = x-a ]; then\n\
\tall=TRUE\n\
fi\n");
if (version != 0) {
fprintf(stderr, "\nCreating version %s shar file.\n\n", version);
fprintf(outfile, "echo shar file version: %s\n", version);
}
if (keeptimes) {
fprintf(stderr, "\nTime stamps will be preserved in both shar and unshar\n\n");
fflush(stderr);
}
fprintf(stderr, "%s:\n",
existed && Append ? "Appending" : "Installing");
fflush(stderr);
++Level;
AddChksum();
if (keeptimes) AddTimeKeeper();
for (c = optind; c < argc; c++) {
if (rflag) {
register char *n = tail(argv[c]);
if (n)
Add(n, 0, NULL);
} else
Add(argv[c], 0, NULL);
}
/* remove the checksum generator and the utime setter */
fprintf(outfile, "rm /tmp/chksum$$.c\n");
fprintf(outfile, "rm /tmp/chksum$$\n");
if (keeptimes) {
fprintf(outfile, "rm /tmp/setutimes$$.c\n");
fprintf(outfile, "rm /tmp/setutimes$$\n");
}
if (Errors)
fprintf(stderr, "\nDone, but I had trouble with %d file%s\n",
Errors, Errors == 1 ? "" : "s");
else
fprintf(stderr, "\nDone.\n");
exit(Errors > 127 ? 127 : Errors);
}
/*
* Return the trailing component of a pathname, sneakily changing directories
* if necessary.
*/
char *
tail(n)
register char *n;
{
register char *p = n, *dir = n;
static elsewhere; /* true => not in WDir */
while (*p == '/')
p++; /* skip any leading '/'s */
if (*n == '/') { /* seems to be absolute; set n to last '/' */
while (*p)
if (*p++ == '/' && *p != '/' && *p)
n = p;
if (n != dir) { /* n is tail of name, dir is directory */
do_chdir: /* chdir to dir, return n */
n[-1] = 0;
if (chdir(dir)) {
error(0, errno,
"can't change to directory \"%s\"", dir);
Errors++;
return (0);
}
elsewhere++;
return (n);
}
}
/* must be relative to original working directory, so go there */
if (elsewhere) {
if (chdir(WDir)) {
error(0, errno, "can't change to directory \"%s\"",
WDir);
Errors++;
return (0);
}
elsewhere = 0;
}
while (*p)
if (*p++ == '/' && *p != '/' && *p)
n = p; /* Find last slash */
if (n != dir)
goto do_chdir;
return (n);
}
/* Set up the ID numbers for Root, Bin, and Daemon */
GetIDs()
{
register struct passwd *p;
struct passwd *getpwnam();
/* RootID = 0; */ /* Already set */
if (p = getpwnam("bin"))
BinID = p->pw_uid;
if (p = getpwnam("daemon"))
DaemonID = p->pw_uid;
endpwent();
}
/*
* Add the named file into the script, performing the right actions depending
* on the type of the file, and changing the owner/mode as appropriate.
*/
Add(fn, cc, cc_out)
register char *fn;
register int cc;
register char *cc_out;
{
struct exec test_exec;
struct stat st;
char *u;
register int level = Level;
if (stat(fn, &st)) {
error(0, errno, "can't access \"%s\"", fn);
Errors++;
return;
}
while (--level > 0)
putc('\t', stderr);
fprintf(stderr, "%s", fn);
if (keeptimes) fprintf(stderr, " (time stamps preserved)");
fflush(stderr);
switch (st.st_mode & S_IFMT) {
case S_IFREG:
if (st.st_size >= sizeof test_exec) {
FILE *fp = fopen(fn, "r");
if (fp == 0) {
fprintf(stderr, ": can't open\n");
fflush(stderr);
Errors++;
return;
}
if (fread(&test_exec, sizeof test_exec, 1, fp) == 1
&& !N_BADMAG(test_exec)) {
fclose(fp);
if (AddExec(fn, " (executable)"))
return;
break;
}
fclose(fp);
}
if (IsDataFile(fn) ? AddExec(fn, " (data)") : AddFile(fn))
return;
break;
case S_IFDIR:
if (AddDir(fn))
return;
break;
case S_IFCHR:
AddNode(fn, "character", st.st_dev);
break;
case S_IFBLK:
AddNode(fn, "block", st.st_dev);
break;
#ifdef S_IFMPC /* not in 4.2 */
case S_IFMPC:
case S_IFMPB:
fprintf(stderr, " Multiplexed!\n");
fflush(stderr);
Errors++;
return;
#endif S_IFMPC
#ifdef S_IFSOCK
case S_IFSOCK:
fprintf(stderr, " Socket!\n");
fflush(stderr);
Errors++;
return;
#endif S_IFSOCK
}
if (st.st_uid == RootID) {
u = "root";
goto changeown;
}
if (st.st_uid == BinID) {
u = "bin";
goto changeown;
}
if (st.st_uid == DaemonID) {
u = "daemon";
changeown:
fprintf(outfile, "\
if [ $all = TRUE ]; then\n\
\techo '\tChanging owner to %s'\n\
\tchown %s %s\n\
else\n\
\techo '\tOriginal owner was %s'\n\
fi\n",
u, u, fn, u);
}
fn = protect(fn);
fprintf(outfile, "\
if [ $made = TRUE ]; then\n\
\tchmod %o %s\n", st.st_mode & 07777, fn);
/* do we need to compile this file? */
if (cc) fprintf(outfile, "\
\tcc -o %s %s\n", cc_out, fn);
/* do we need to preserve time stamps? */
if (keeptimes) fprintf(outfile, "\
\t/tmp/setutimes$$ %ld %ld %s\n", st.st_atime, st.st_mtime, fn);
fprintf(outfile, "\
\techo -n '\t'; ls -ld %s\n\
fi\n", fn);
if ((st.st_mode & S_IFMT) != S_IFDIR) {
putc('\n', stderr);
fflush(stderr);
}
}
/* Add a character or block device */
AddNode(fn, type, dev)
register char *fn, *type;
dev_t dev;
{
register int ma = major(dev), mi = minor(dev);
fn = protect(fn);
fprintf(stderr, " (%s special)", type);
fflush(stderr);
fprintf(outfile, "\
if [ $all = TRUE ]; then\n\
\techo Making %s special device %s number '(%d, %d)'\n\
\tmknod %s %c %d %d\n\
made=TRUE\n\
else\n\
\techo Not making %s special device %s number '(%d, %d)'\n\
made=FALSE\n\
fi\n",
type, fn, ma, mi, fn, *type, ma, mi, type, fn, ma, mi);
}
AddFile(fn)
register char *fn;
{
register int c, wantx = 1;
register FILE *fp = fopen(fn, "r");
register int wc;
int chk;
if (fp == NULL) {
fprintf(stderr, ": can't open\n");
fflush(stderr);
Errors++;
return (-1);
}
fn = protect(fn);
chk = getchksum(fn);
fprintf(outfile, "\
echo Extracting %s\n\
sed 's/^X//' <<'//go.sysin dd *' >%s\n",
fn, fn);
wc = 0;
while ((c = getc(fp)) != EOF) {
if (wantx) {
if (c == 'X' || c == '.' || c == '/' || c == 'F')
putc('X', outfile);
wantx = 0;
}
putc(c, outfile);
if (c == '\n')
wantx++;
wc++;
}
fclose(fp);
if (!wantx) { /* oops, have to add a newline */
fprintf(stderr, " [incomplete last line]");
fflush(stderr);
putc('\n', outfile);
wc++;
}
fprintf(outfile, "//go.sysin dd *\n");
fprintf(outfile, "if [ `wc -c < %s` != %d ]; then\n\
\tmade=FALSE\n\
\techo error transmitting %s --\n\
\techo length should be %d, not `wc -c < %s`\n\
else\n\
\tmade=TRUE\n\
fi\n",
fn, wc, fn, wc, fn);
fprintf(outfile, "if [ `/tmp/chksum$$ %s` != %d ]; then\n\
\tmade=FALSE\n\
\techo checksum is incorrect for %s --\n\
\techo checksum should be %d, not `/tmp/chksum$$ %s`\n\
else\n\
\tmade=TRUE\n\
fi\n", fn, chk, fn, chk, fn);
return (0);
}
/* Return true iff the file 'fn' is a data file */
IsDataFile(fn)
char *fn;
{
register int n, l;
int pipes[2];
char buf[201];
if (AssumeNoDataFiles)
return (0);
if (pipe(pipes))
return (-1); /* call it data anyway */
fflush(stderr);
if (vfork() == 0) {
close(0);
dup2(pipes[1], 1);
close(pipes[0]);
close(pipes[1]);
execl("/usr/bin/file", "file", fn, 0);
fprintf(stderr, " can't exec /usr/bin/file");
fflush(stderr);
_exit(0);
}
close(pipes[1]);
l = 0;
while ((n = sizeof buf - l - 1) > 0) {
if ((n = read(pipes[0], buf + l, n)) <= 0)
break;
l += n;
}
close(pipes[0]);
buf[l] = 0;
while (wait((int *)0) != -1)
;
if (strlen(buf) <= (l = strlen(fn)))
return (1);
if (contains(buf + l, "text"))
return (0);
if (contains(buf + l, "script"))
return (0);
if (contains(buf + l, "commands"))
return (0);
return (1);
}
/* Write stuff to extract an executable */
AddExec(fn, type)
register char *fn;
char *type;
{
char *pfn = protect(fn);
fprintf(stderr, type);
fflush(stderr);
fprintf(outfile,
"echo Decoding %s\nuudecode << '//go.sysin dd *'\n",
pfn);
fflush(outfile);
if (vfork() == 0) {
(void) dup2(fileno(outfile), 1);
execl("/usr/bin/uuencode", "uuencode", fn, fn, 0);
fprintf(stderr, "Help! Can't exec /usr/bin/uuencode\n");
exit(0);
}
while (wait((int *)0) != -1)
;
fprintf(outfile, "//go.sysin dd *\nmade=TRUE\n");
return (0);
}
/* Compare function for qsort */
DirCmp(d1, d2)
register struct direct *d1, *d2;
{
#ifndef LIBNDIR
return (strncmp(d1->d_name, d2->d_name, DIRSIZ));
#else LIBNDIR
return (strncmp(d1->d_name, d2->d_name,
max(d1->d_namlen, d2->d_namlen)));
#endif LIBNDIR
}
/* Add a directory: recursion time */
#ifndef LIBNDIR
AddDir(fn)
char *fn;
{
int f;
struct stat st;
char nmbuf[BUFSIZ];
register char *cp;
struct direct *d;
register struct direct *p, *dp, *pp;
char *pfn = protect(fn);
fprintf(outfile,
"echo Making directory %s\nmkdir %s\n", pfn, pfn);
if ((f = open(fn, 0)) < 0) {
fprintf(stderr, ": can't open\n");
fflush(stderr);
Errors++;
return (-1);
}
fprintf(stderr, ": directory");
fflush(stderr);
fstat(f, &st);
if (st.st_size == 0) {
fprintf(stderr, " (empty!)");
fflush(stderr);
close(f);
return (0);
}
d = (struct direct *) malloc(st.st_size);
pp = dp = (struct direct *) malloc(st.st_size);
if (read(f, (char *) d, st.st_size) != st.st_size) {
fprintf(stderr, ": read error\n");
fflush(stderr);
close(f);
Errors++;
return (-1);
}
close(f);
putc('\n', stderr);
fflush(stderr);
Level++;
for (p = d; p < &d[st.st_size / sizeof *d]; p++) {
if (p->d_name[0] == '.') {
if (p->d_name[1] == 0)
continue;
if (p->d_name[1] == '.' && p->d_name[2] == 0)
continue;
}
if (p->d_ino)
*pp++ = *p;
}
free(d);
qsort(dp, pp - dp, sizeof *dp, DirCmp);
for (cp = nmbuf; *cp++ = *fn++;)
;
cp[-1] = '/';
for (p = dp; p < pp; p++) {
strncpy(cp, p->d_name, DIRSIZ);
cp[DIRSIZ] = 0;
Add(nmbuf, 0, NULL);
}
Level--;
free(dp);
fprintf(outfile, "made=TRUE\n");
return (0);
}
#else LIBNDIR
AddDir(fn)
char *fn;
{
int f, g;
char nmbuf[BUFSIZ];
char *cp, *pfn;
struct direct *d, *dp, *p;
DIR *dd;
pfn = protect(fn);
fprintf(outfile,
"echo Making directory %s\nmkdir %s\n", pfn, pfn);
if ((dd = opendir(fn)) == NULL) {
fprintf(stderr, ": can't open\n");
fflush(stderr);
Errors++;
return (-1);
}
fprintf(stderr, ": directory");
fflush(stderr);
for (f = 0; p = readdir(dd); f++)
continue;
if (f == 0) {
empty:
fprintf(stderr, " (empty)");
fflush(stderr);
closedir(dd);
return (0);
}
again:
f += 10;
d = (struct direct *) malloc((unsigned) (f * sizeof(struct direct)));
rewinddir(dd);
for (dp = d, g = 0; p = readdir(dd);) {
if ((p->d_namlen == 1 && !strcmp(p->d_name, "."))
|| (p->d_namlen == 2 && !strcmp(p->d_name, "..")))
continue;
if (f <= g++) { /* directory grew!!! */
for (f = g; p = readdir(dd); f++)
continue;
goto again;
}
dp->d_ino = p->d_ino;
dp->d_reclen = sizeof(struct direct);
dp->d_namlen = p->d_namlen;
strcpy(dp->d_name, p->d_name);
dp++;
}
if (d == dp) {
free(d);
goto empty;
}
closedir(dd);
putc('\n', stderr);
fflush(stderr);
Level++;
qsort(d, dp - d, sizeof *dp, DirCmp);
for (cp = nmbuf; *cp++ = *fn++;)
;
cp[-1] = '/';
for (p = d; p < dp; p++) {
strcpy(cp, p->d_name);
Add(nmbuf, 0, NULL);
}
Level--;
free(d);
fprintf(outfile, "made=TRUE\n");
return (0);
}
#endif LIBNDIR
/* Return true if "little" is contained within "big" */
contains(big, little)
register char *big, *little;
{
register char *bp, *lp;
while (*big) {
if (*big++ != *little)
continue;
bp = big, lp = little + 1;
while (*lp)
if (*bp++ != *lp++)
goto cont;
return (1);
cont: ;
}
return (0);
}
/*
* Protect against meta-characters in file names.
*/
char *
protect(fn)
register char *fn;
{
static char pfn[BUFSIZ];
register char *p = pfn;
while (*p = *fn++) {
switch (*p++) {
case '*': case '[': case ']': case '?':
case ';': case '|': case '`': case '\\':
case '$': case '^': case '&': case '<':
case '>': case '#': case '(': case ')':
case ' ': case '\t':
p[-1] = '\\';
*p++ = fn[-1];
}
}
*p = 0;
return (pfn);
}
/* Alan Ishigo */
int getchksum(fn)
char *fn;
{
FILE *f;
int acc;
char c;
acc = 0;
f = fopen(fn, "r");
if (f == NULL) {
fprintf(stderr, "Unable to read %s for checksum calculation!\n", fn);
return(0);
}
while ((c = getc(f)) != EOF) {
acc = acc + c;
acc = acc << 1;
while (acc < 0) acc = shift(acc);
}
fclose(f);
return(acc);
} /* getchksum */
int shift(n)
int n;
{
int val;
val = n << 1;
if (n < 0) val = val | 01;
return(val);
} /* shift */
/*
* error - University of Maryland specific (sigh)
*
* Useful for printing error messages. Will print the program name
* and (optionally) the system error associated with the values in
* <errno.h>.
*
* Note that the type (and even the existence!) of ``arg'' is undefined.
*/
error(quit, e, fmt, arg)
int quit;
register int e;
char *fmt;
{
extern char *sys_errlist[];
extern int sys_nerr;
register char *p = _argv0;
if (p != NULL) {
#ifdef optional
char *s, *rindex();
if ((s = rindex(p, '/')) != NULL)
p = s + 1;
#endif
(void) fprintf(stderr, "%s: ", p);
}
_doprnt(fmt, &arg, stderr); /* magic */
if (e > 0) {
if (e < sys_nerr)
(void) fprintf(stderr, ": %s", sys_errlist[e]);
else
(void) fprintf(stderr, ": unknown error number %d", e);
}
(void) putc('\n', stderr);
(void) fflush(stderr);
if (quit)
exit(quit);
}
AddTimeKeeper()
{
fprintf(outfile, "\
echo Time stamps have been preserved\n\n\
echo Extracting /tmp/setutimes$$.c\n\
sed 's/^X//' <<'//go.sysin dd *' >/tmp/setutimes$$.c\n\n\
#include <stdio.h>\n\
#include <sys/types.h>\n\
#include <sys/time.h>\n\n\n\
main(argc, argv)\n\
int argc;\n\
char *argv[];\n\
{\n\
\ttime_t times[2];\n\
\tint n;\n\n\
\tif (argc < 3) {\n\
\t\tfprintf(stderr, \"Invalid number of arguments.\\n\");\n\
\t\texit(-1);\n\
\t\t}\n\n\
\tn = sscanf(argv[1], \"%%ld\", ×[0]);\n\
\tn = sscanf(argv[2], \"%%ld\", ×[1]);\n\n\
\tif (utime(argv[3], times) < 0) {\n\
\t\tfprintf(stderr, \"Unable to set times.\\n\");\n\
\t\texit(-2);\n\
\t\t}\n\n\
} /* main */\n\n\n\n\
//go.sysin dd *\n\
if [ `wc -c < /tmp/setutimes$$.c` != 417 ]; then\n\
\tmade=FALSE\n\
\techo error transmitting /tmp/setutimes$$.c --\n\
\techo length should be 417, not `wc -c < /tmp/setutimes$$.c`\n\
else\n\
\tmade=TRUE\n\
fi\n\
if [ $made = TRUE ]; then\n\
\tchmod 664 /tmp/setutimes$$.c\n\
\techo Compiling /tmp/setutimes$$\n\
\tcc -O -o /tmp/setutimes$$ /tmp/setutimes$$.c\n\
\techo -n ' '; ls -ld /tmp/setutimes$$.c\n\
\techo -n ' '; ls -ld /tmp/setutimes$$\n\
fi\n");
}
AddChksum()
{
fprintf(outfile, "\
echo Checksum program\n\
echo Extracting /tmp/chksum$$.c\n\
sed 's/^X//' <<'//go.sysin dd *' >/tmp/chksum$$.c\n\n\
#include <stdio.h>\n\
main(argc, argv)\n\
int argc;\n\
char *argv[];\n\
{\n\
\tFILE *f;\n\
\tint acc;\n\
\tchar c;\n\n\
\tacc = 0;\n\
\tf = fopen(argv[1], \"r\");\n\
\tif (f == NULL) {\n\
\t\tfprintf(stdout, \"%%d\\n\", 0);\n\
\t\texit(-1);\n\
\t\t}\n\
\twhile ((c = getc(f)) != EOF) {\n\
\t\tacc = acc + c;\n\
\t\tacc = acc << 1;\n\
\t\twhile (acc < 0) acc = shift(acc);\n\
\t\t}\n\
\tfclose(f);\n\
\tfprintf(stdout, \"%%d\\n\", acc);\n\
\texit(acc);\n\
}\n\n\
int shift(n)\n\
int n;\n\
{\n\
\tint val;\n\n\
\tval = n << 1;\n\
\tif (n < 0) val = val | 01;\n\
\treturn(val);\n\
}\n\n\
//go.sysin dd *\n\
if [ `wc -c < /tmp/chksum$$.c` != 451 ]; then\n\
\tmade=FALSE\n\
\techo error transmitting /tmp/chksum$$.c --\n\
\techo length should be 451, not `wc -c < /tmp/chksum$$.c`\n\
else\n\
\tmade=TRUE\n\
fi\n\
if [ $made = TRUE ]; then\n\
\tchmod 664 /tmp/chksum$$.c\n\
\techo Compiling /tmp/chksum$$\n\
\tcc -O -o /tmp/chksum$$ /tmp/chksum$$.c\n\
\techo -n ' '; ls -ld /tmp/chksum$$.c\n\
\techo -n ' '; ls -ld /tmp/chksum$$\n\
fi\n");
}